﻿using System;
#if __IOS__
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Reflection;
using System.Threading.Tasks;
using CatLib.ILRuntime.Redirect;
using ILRuntime.CLR.Method;
using ILRuntime.Runtime.Intepreter;
using ILRuntime.Runtime.Stack;

namespace mb
{
    unsafe class ILRLoader : IHotLoader
    {

        const string EntryClassName = "hotfix.Entry";
        const string EntryMethodName = "Start";

        RedirectMapping mapping = new RedirectMapping();
        public static ILRuntime.Runtime.Enviorment.AppDomain appDomain = new ILRuntime.Runtime.Enviorment.AppDomain();

        public object CreateInstance(string fullName)
        {
            return appDomain.Instantiate(fullName);
        }

        public void Load(byte[] dll, byte[] symbol)
        {
            Console.WriteLine("load hotfix ilruntime");

#if DEBUG && (UNITY_EDITOR || UNITY_ANDROID || UNITY_IPHONE)
            appDomain.UnityMainThreadID = System.Threading.Thread.CurrentThread.ManagedThreadId;
#endif
            System.IO.MemoryStream fs = new MemoryStream(dll);
            System.IO.MemoryStream fsSymbol = new MemoryStream(symbol);
            appDomain.LoadAssembly(fs, fsSymbol, new ILRuntime.Mono.Cecil.Pdb.PdbReaderProvider());

            mapping.Register("Register", 2, 0, ILRRedirect.Register_TService_TConcrete);
            mapping.Register("Get", 1, 0, ILRRedirect.Get_TService);

#if _PROTOBUF_NET
            ProtoBuf.PType.RegisterFunctionCreateInstance(typeName => appDomain.Instantiate(typeName));
            ProtoBuf.PType.RegisterFunctionGetRealType(o =>
            {
                var type = o.GetType();
                if (type.FullName == "ILRuntime.Runtime.Intepreter.ILTypeInstance")
                {
                    var ilo = o as ILRuntime.Runtime.Intepreter.ILTypeInstance;
                    type = ProtoBuf.PType.FindType(ilo.Type.FullName);
                }
                return type;
            });
#endif

            Register(appDomain);

            ILRuntime.Runtime.Generated.CLRBindings.Initialize(appDomain);
            ILRuntime.Runtime.Generated.CLRAdaptorRegister.Initialize(appDomain);
            ILRuntime.Runtime.Generated.CLRDelegateRegister.Initialize(appDomain);
            LitJson.JsonMapper.RegisterILRuntimeCLRRedirection(appDomain);

            appDomain.Invoke(EntryClassName, EntryMethodName, null, null);
        }

        void Register(ILRuntime.Runtime.Enviorment.AppDomain appDomain)
        {
            var methods = typeof(ServiceLocator).GetMethods();
            foreach( var method in methods ) {
                var redirection = mapping.GetRedirection(method);
                if( redirection == null ) {
                    continue;
                }

                appDomain.RegisterCLRMethodRedirection(method, redirection);
            }

            appDomain.DelegateManager.RegisterMethodDelegate<int>();
            appDomain.DelegateManager.RegisterMethodDelegate<System.Object, System.EventArgs>();
            appDomain.DelegateManager.RegisterDelegateConvertor<System.EventHandler>((act) =>
            {
                return new System.EventHandler((sender, e) =>
                {
                    ( (Action<System.Object, System.EventArgs>) act )(sender, e);
                });
            });

            appDomain.DelegateManager.RegisterMethodDelegate<System.Object, UIKit.UITabBarSelectionEventArgs>();
            appDomain.DelegateManager.RegisterDelegateConvertor<System.EventHandler<UIKit.UITabBarSelectionEventArgs>>((action) =>
            {
                return new System.EventHandler<UIKit.UITabBarSelectionEventArgs>((sender, e) =>
                {
                    ( (Action<System.Object, UIKit.UITabBarSelectionEventArgs>) action )(sender, e);

                });
            });

            appDomain.DelegateManager.RegisterFunctionDelegate<UIKit.UIGestureRecognizer, UIKit.UIGestureRecognizer, System.Boolean>();

            appDomain.DelegateManager.RegisterMethodDelegate<System.Object, System.Timers.ElapsedEventArgs>();
            appDomain.DelegateManager.RegisterDelegateConvertor<System.Timers.ElapsedEventHandler>((action) =>
            {
                return new System.Timers.ElapsedEventHandler((sender, e) =>
                {
                    ( (Action<System.Object, System.Timers.ElapsedEventArgs>) action )(sender, e);
                });
            });

            appDomain.DelegateManager.RegisterFunctionDelegate<UIKit.UITextField, System.Boolean>();
            appDomain.DelegateManager.RegisterMethodDelegate<System.Boolean>();
            appDomain.DelegateManager.RegisterMethodDelegate<System.Object>();

            appDomain.DelegateManager.RegisterFunctionDelegate<mb.iOS.MyCollectionView, Foundation.NSIndexPath, UIKit.UICollectionViewLayoutAttributes, UIKit.UICollectionViewLayoutAttributes>();
            appDomain.DelegateManager.RegisterFunctionDelegate<System.ValueTuple<UIKit.UIView, System.Int32, System.Int32>, System.Int32>();
            appDomain.DelegateManager.RegisterFunctionDelegate<System.ValueTuple<UIKit.UIView, System.Int32, System.Int32, System.Int32>, System.Int32>();
            appDomain.DelegateManager.RegisterFunctionDelegate<ILRuntime.Runtime.Intepreter.ILTypeInstance, System.Boolean>();
            appDomain.DelegateManager.RegisterFunctionDelegate<ILRuntime.Runtime.Intepreter.ILTypeInstance, System.Int32>();
            appDomain.DelegateManager.RegisterMethodDelegate<Foundation.NSObservedChange>();

            appDomain.DelegateManager.RegisterMethodDelegate<System.Boolean, System.Int32, System.String>();
            appDomain.DelegateManager.RegisterMethodDelegate<System.Int64, System.Int64>();
            appDomain.DelegateManager.RegisterMethodDelegate<Masonry.ConstraintMaker>();
            appDomain.DelegateManager.RegisterMethodDelegate<System.Object, System.EventArgs>();
            appDomain.DelegateManager.RegisterMethodDelegate<mb.iOS.MyCollectionView>();
            appDomain.DelegateManager.RegisterMethodDelegate<mb.iOS.MyCollectionView, Foundation.NSIndexPath>();
            appDomain.DelegateManager.RegisterMethodDelegate<UIKit.UIAlertAction>();
            appDomain.DelegateManager.RegisterMethodDelegate<ILRuntime.Runtime.Intepreter.ILTypeInstance>();
            appDomain.DelegateManager.RegisterMethodDelegate<System.Object, System.Timers.ElapsedEventArgs>();
            appDomain.DelegateManager.RegisterFunctionDelegate<System.Boolean>();
            appDomain.DelegateManager.RegisterFunctionDelegate<mb.iOS.MyScrollView, UIKit.UIGestureRecognizer, UIKit.UIGestureRecognizer, System.Boolean>();
            appDomain.DelegateManager.RegisterFunctionDelegate<mb.iOS.MyCollectionView, System.Int32, System.Int32>();
            appDomain.DelegateManager.RegisterFunctionDelegate<mb.iOS.MyCollectionView, Foundation.NSIndexPath, mb.iOS.MyCollectionViewCell>();
            appDomain.DelegateManager.RegisterFunctionDelegate<mb.iOS.MyCollectionView, UIKit.UICollectionViewLayout, System.Int32, System.Int32>();
            appDomain.DelegateManager.RegisterFunctionDelegate<mb.iOS.MyCollectionView, UIKit.UICollectionViewLayout, System.Int32, UIKit.UIEdgeInsets>();
            appDomain.DelegateManager.RegisterFunctionDelegate<mb.iOS.MyCollectionView, UIKit.UICollectionViewLayout, Foundation.NSIndexPath, CoreGraphics.CGSize>();
            appDomain.DelegateManager.RegisterFunctionDelegate<UIKit.UITextField, System.Boolean>();
            appDomain.DelegateManager.RegisterFunctionDelegate<System.String, System.ValueTuple<System.Int32, System.Int64, System.Int64>>();
            appDomain.DelegateManager.RegisterFunctionDelegate<System.ValueTuple<System.Int32, System.Int64, System.Int64>, System.Boolean>();

            appDomain.DelegateManager.RegisterMethodDelegate<Xamarin.Forms.View>();

            appDomain.DelegateManager.RegisterDelegateConvertor<System.EventHandler>((action) =>
            {
                return new System.EventHandler((sender, e) =>
                {

                    ( (Action<System.Object, System.EventArgs>) action )(sender, e);

                });
            });

            appDomain.DelegateManager.RegisterDelegateConvertor<System.Threading.ThreadStart>((act) =>
            {
                return new System.Threading.ThreadStart(() =>
                {
                    ( (Action) act )();
                });
            });

            appDomain.DelegateManager.RegisterDelegateConvertor<System.Predicate<System.ValueTuple<System.Int32, System.Int64, System.Int64>>>((act) => { return new System.Predicate<System.ValueTuple<System.Int32, System.Int64, System.Int64>>((obj) => { return ( (Func<System.ValueTuple<System.Int32, System.Int64, System.Int64>, System.Boolean>) act )(obj); }); });

            appDomain.DelegateManager.RegisterFunctionDelegate<global::Adapt_IMessage.Adaptor, System.Byte[]>();

            appDomain.DelegateManager.RegisterFunctionDelegate<System.Text.RegularExpressions.Match, System.String>();
            appDomain.DelegateManager.RegisterDelegateConvertor<System.Text.RegularExpressions.MatchEvaluator>((act) => { return new System.Text.RegularExpressions.MatchEvaluator((match) => { return ( (Func<System.Text.RegularExpressions.Match, System.String>) act )(match); }); });
        }
    }

    public unsafe class ILRRedirect
    {

        public static StackObject* Register_TService_TConcrete(ILIntepreter intp, StackObject* __esp, IList<object> mStack, CLRMethod method, bool isNewObj)
        {
            StackObject* __ret = ILIntepreter.Minus(__esp, 1);
            var genericArguments = method.GenericArguments;
            if( genericArguments == null || genericArguments.Length != 2 || method.ParameterCount != 0 ) {
                throw new EntryPointNotFoundException();
            }

            var tService = Helper.ITypeToService(genericArguments[0]);
            var tType = Helper.ITypeToClrType(genericArguments[1]);

            ServiceLocator.Instance.Register(tService, tType);
            return __ret;
        }

        public static StackObject* Get_TService(ILIntepreter intp, StackObject* __esp, IList<object> mStack,
            CLRMethod method, bool isNewObj)
        {
            StackObject* __ret = ILIntepreter.Minus(__esp, 1);

            var genericArguments = method.GenericArguments;
            if( genericArguments == null || genericArguments.Length != 1 || method.ParameterCount != 0 ) {
                throw new EntryPointNotFoundException();
            }

            var tService = Helper.ITypeToService(genericArguments[0]);

            return ILIntepreter.PushObject(__ret, mStack, ServiceLocator.Instance.Get(tService));
        }
    }
}

[AttributeUsage(AttributeTargets.Class)] public class ILRAdaptorAttribute : Attribute { }
namespace ILRuntime.Runtime.Generated
{
    public partial class CLRAdaptorRegister
    {
        public static void Initialize(ILRuntime.Runtime.Enviorment.AppDomain appDomain)
        {
            var assembly = Assembly.GetExecutingAssembly();
            foreach( Type type in assembly.GetTypes() ) {
                if( type.GetCustomAttributes(typeof(ILRAdaptorAttribute), true).Length > 0 ) {
                    appDomain.RegisterCrossBindingAdaptor(Activator.CreateInstance(type) as Enviorment.CrossBindingAdaptor);
                }
            }
        }
    }

    partial class CLRDelegateRegister
    {
        static partial void _Initialize(ILRuntime.Runtime.Enviorment.AppDomain appDomain);
        public static void Initialize(ILRuntime.Runtime.Enviorment.AppDomain appDomain)
        {
            _Initialize(appDomain);
        }
    }

    partial class CLRBindings
    {
        static partial void _Initialize(ILRuntime.Runtime.Enviorment.AppDomain appDomain);
        public static void Initialize(ILRuntime.Runtime.Enviorment.AppDomain appDomain)
        {
            _Initialize(appDomain);
        }
    }
}
#else

namespace ILRuntime.Other
{
    [AttributeUsage(AttributeTargets.Class | AttributeTargets.Interface)] public class NeedAdaptorAttribute : Attribute { }
}

#endif